import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import {
  Repository,
  Connection,
  getRepository,
  Between,
  Brackets,
  getConnection,
} from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { UsersEntity } from './users.entity';
import { MailerService } from '@nest-modules/mailer';
//jwt
import { JwtService } from '@nestjs/jwt';
import { CaptchaService } from '../captcha/captcha.service';
import { getCaptcha } from '../../utils/index';
import * as dayjs from 'dayjs';
import { CustomEntity } from '../custom/custom.entity';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(UsersEntity)
    private readonly usersRepository: Repository<UsersEntity>,
    private connection: Connection,
    private readonly jwtService: JwtService,
    private readonly mailerService: MailerService,
    private readonly captchaService: CaptchaService,
  ) {}
  //验证用户
  async validateUser(param) {
    return await this.usersRepository.findOne({ where: { ...param } });
  }
  async isUserRegister(param) {
    let user = await this.validateUser(param);
    if (user) {
      throw new HttpException(
        {
          message: '邮箱已注册',
          error: '邮箱已注册',
        },
        HttpStatus.BAD_REQUEST,
      );
    }
  }
  //登录
  async login(data): Promise<any> {
    return {
      ...data,
      message: '登录成功',
      token: this.jwtService.sign({
        id: data.id,
        name: data.name,
        email: data.email,
      }),
    };
  }
  //登出
  async loginOut(param): Promise<any> {
    await this.usersRepository.save({ ...param, isLogin: false });
    return {
      message: '已退出登录',
    };
  }

  //更新可以为空
  async updateUser(param): Promise<any> {
    const { access, area = [], ...restParam } = param;
    if (access) {
      restParam.access = access.map(id => {
        return { id };
      });
    }
    return await this.usersRepository.save({
      province: area?.[0],
      city: area?.[1],
      ...restParam,
    });
  }
  //获取注册验证码
  async getRegisterCaptcha(param) {
    await this.isUserRegister(param);
    return await this.sendEmailCaptcha(param);
  }
  //发送邮箱验证码
  async sendEmailCaptcha({ email }) {
    let captcha = getCaptcha();
    let validTime = dayjs()
      .add(5, 'minute')
      .format('YYYY-MM-DD HH:mm:ss');
    await this.captchaService.saveCaptcha({ captcha, key: email, validTime });
    this.mailerService.sendMail({
      to: email,
      from: '1931740796@qq.com',
      subject: '[Crm销管系统] 这是您的邮箱验证码',
      template: './emailCaptcha',
      context: {
        captcha,
        email,
      },
    });
    return { success: true };
  }

  //注册
  async register(param) {
    await this.isUserRegister({ email: param?.email });
    await this.captchaService.validateCaptcha({ ...param, key: param.email });
    param.createDate = dayjs().format('YYYY-MM-DD HH:mm:ss');
    let data = await this.usersRepository.save(param);
    return {
      message: '注册成功，正在生成用户信息。请尝试在10秒后登录。',
      token: this.jwtService.sign({
        id: data.id,
        name: data.name,
        email: data.email,
      }),
    };
  }
  //查找用户关联所有表
  async findUser(param): Promise<UsersEntity> {
    return await this.usersRepository.findOne({
      relations: ['photos', 'access', 'custom'],
      where: { ...param },
      // where: { ...param, access: { order: 'order' } },
    });
  }
  //保存不能为空
  async saveUser(param): Promise<any> {
    const {
      access,
      job,
      area,
      department,
      birthDate,
      sex,
      ...restParam
    } = param;
    const connection = getConnection();
    access &&
      (restParam.access = access.map(id => {
        return { id };
      }));
    job &&
      (restParam.job = job.map(id => {
        return { id };
      }));

    area && (restParam.province = area?.[0] || null);
    area && (restParam.city = area?.[1] || null);

    if (!restParam.id) {
      restParam.createDate = dayjs().format('YYYY-MM-DD HH:mm:ss');
    }

    let data = await this.usersRepository.save({
      birthDate: birthDate || null,
      sex: sex || null,
      department: department || null,
      ...restParam,
    });
    let custom = [];
    
    //自定义字段整理
    await Promise.all(
      Object.keys(restParam).map(async (item: string) => {
        if (item.indexOf('custom_') !== -1) {
          let customItem = new CustomEntity();
          customItem.id = `${data.id}_${item}`;
          customItem.field = item;
          customItem.value = JSON.stringify(restParam[item]);
          custom.push(customItem);
          return await connection.manager.save(customItem);
        }
      }),
    );
    if (custom.length) {
      data.custom = custom;
      return await this.usersRepository.save(data);
    } else {
      return data;
    }
  }
  //用户表格查询
  async getUserList(param) {
    const {
      current,
      pageSize,
      noPagination,
      orderParam,
      createDateRange,
      birthDateRange,
      accessIds,
      jobIds,
      departmentIds,
      ...restParam
    } = param;
    //排序参数

    let where = {
      ...restParam,
      createDate: Between(createDateRange?.[0], createDateRange?.[1]),
      birthDate: Between(birthDateRange?.[0], birthDateRange?.[1]),
    };
    if (!createDateRange) {
      delete where.createDate;
    }
    if (!birthDateRange) {
      delete where.birthDate;
    }
    //排序逻辑
    const order = {};
    Object.keys(orderParam || {}).map(item => {
      order[`user.${item}`] = orderParam[item];
    });
    let data = await this.usersRepository
      .createQueryBuilder('user')
      .where(where)
      .leftJoinAndSelect('user.photos', 'photo')
      .leftJoinAndSelect('user.access', 'access')
      .leftJoinAndSelect('user.province', 'provinceUsers')
      .leftJoinAndSelect('user.city', 'cityUsers')
      .leftJoinAndSelect('user.job', 'job')
      .leftJoinAndSelect('user.department', 'department')
      .leftJoinAndSelect('user.custom', 'custom')
      .andWhere(qb => {
        const subQuery = qb
          .subQuery()
          .select('user.id')
          .from(UsersEntity, 'user')
          .leftJoin('user.access', 'access')
          .andWhere(
            new Brackets(qb => {
              if (accessIds) {
                return qb.where('access.id IN (:...accessIds)', { accessIds });
              }
            }),
          )
          .getQuery();
        return 'user.id IN ' + subQuery;
      })
      .andWhere(qb => {
        const subQuery = qb
          .subQuery()
          .select('user.id')
          .from(UsersEntity, 'user')
          .leftJoin('user.job', 'job')
          .andWhere(
            new Brackets(qb => {
              if (jobIds) {
                return qb.where('job.id IN (:...jobIds)', { jobIds });
              }
            }),
          )
          .getQuery();
        return 'user.id IN ' + subQuery;
      })
      .andWhere(qb => {
        const subQuery = qb
          .subQuery()
          .select('user.id')
          .from(UsersEntity, 'user')
          .leftJoin('user.department', 'department')
          .andWhere(
            new Brackets(qb => {
              if (departmentIds) {
                return qb.where('department.id IN (:...departmentIds)', {
                  departmentIds,
                });
              }
            }),
          )
          .getQuery();
        return 'user.id IN ' + subQuery;
      })
      // .andWhere(
      //   new Brackets(qb => {
      //     if (accessIds) {
      //       return qb.where('access.state = 1 ');
      //     } else {
      //       return qb.where('access.state = 1 OR access.state is null ' );
      //     }
      //   }),
      // )
      .orderBy(order)
      .skip(noPagination ? undefined : (current - 1) * pageSize)
      .take(noPagination ? undefined : pageSize)
      .getManyAndCount();

    return { data: data[0], total: data[1], current, pageSize };
  }
  //获取用户
  async getUser(param) {
    return await this.usersRepository
      .createQueryBuilder('user')
      .addSelect('user.password')
      .leftJoinAndSelect('user.photos', 'photo')
      .leftJoinAndSelect('user.access', 'access')
      .leftJoinAndSelect('user.province', 'provinceUsers')
      .leftJoinAndSelect('user.city', 'cityUsers')
      .leftJoinAndSelect('user.job', 'job')
      .leftJoinAndSelect('user.department', 'department')
      .leftJoinAndSelect('user.custom', 'custom')
      .where(param)
      // .andWhere('access.state = 1 OR access.state is null')
      .getOne();
  }
  // 删除用户
  async delUserList(param) {
    const { idList } = param;
    let data = await this.usersRepository.findByIds(idList);
    return await this.usersRepository.remove(data);
  }

  //===================下为模板接口===========================
  async findAll(): Promise<UsersEntity[]> {
    // relations: ['photos']， 联合查询
    return await this.usersRepository.find({ relations: ['photos'] });

    // 或者使用queryBuilder
    // return await getRepository(UsersEntity)
    //   .createQueryBuilder("user")
    //   .leftJoinAndSelect("user.photos", "photo")
    //   .getMany()
  }

  async create(user): Promise<UsersEntity[]> {
    const { name } = user;
    const u = await getRepository(UsersEntity).findOne({ where: { name } });
    //   .createQueryBuilder('users')
    //   .where('users.name = :name', { name });
    // const u = await qb.getOne();
    if (u) {
      throw new HttpException(
        {
          message: 'Input data validation failed',
          error: 'name must be unique.',
        },
        HttpStatus.BAD_REQUEST,
      );
    }
    return await this.usersRepository.save(user);
  }

  async createMany(users: UsersEntity[]) {
    const queryRunner = this.connection.createQueryRunner();

    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      users.forEach(async user => {
        await queryRunner.manager.getRepository(UsersEntity).save(user);
      });

      await queryRunner.commitTransaction();
    } catch (err) {
      // since we have errors lets rollback the changes we made
      await queryRunner.rollbackTransaction();
    } finally {
      // you need to release a queryRunner which was manually instantiated
      await queryRunner.release();
    }
  }
}
